////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 by EMC Corporation, All Rights Reserved
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is EMC Corporation
///
/// @author Andrey Abramov
////////////////////////////////////////////////////////////////////////////////

#pragma once

namespace fst {

// This class discards any implicit matches (e.g., the implicit epsilon
// self-loops in the SortedMatcher). Matchers are most often used in
// composition/intersection where the implicit matches are needed
// e.g. for epsilon processing. However, if a matcher is simply being
// used to look-up explicit label matches, this class saves the user
// from having to check for and discard the unwanted implicit matches
// themselves.
template <class MatcherImpl>
class ExplicitMatcherExt final : public MatcherBase<typename MatcherImpl::FST::Arc> {
public:
    typedef typename MatcherImpl::FST FST;
    typedef typename FST::Arc Arc;
    typedef typename Arc::Label Label;
    typedef typename Arc::StateId StateId;
    typedef typename Arc::Weight Weight;

    template <typename... Args>
    explicit ExplicitMatcherExt(Args &&...args)
        : matcher_(std::forward<Args>(args)...), match_type_(matcher_.Type(false)) // read type without checks
    {}

    ExplicitMatcherExt(const ExplicitMatcherExt &rhs, bool safe = false) : matcher_(rhs.matcher_, safe), match_type_(rhs.match_type_) {}

    ExplicitMatcherExt *Copy(bool safe = false) const final { return new ExplicitMatcherExt(*this, safe); }

    MatchType Type(bool test) const final { return matcher_.Type(test); }

    void SetState(StateId s) final { matcher_.SetState(s); }

    bool Find(Label match_label) final {
        matcher_.Find(match_label);
        CheckArc();
        return !Done();
    }

    bool Done() const final { return matcher_.Done(); }

    const Arc &Value() const final { return matcher_.Value(); }

    void Next() final {
        matcher_.Next();
        CheckArc();
    }

    Weight Final(StateId s) const final { return matcher_.Final(s); }

    ssize_t Priority(StateId s) final { return matcher_.Priority(s); }

    const FST &GetFst() const final { return matcher_.GetFst(); }

    std::uint64_t Properties(std::uint64_t inprops) const final { return matcher_.Properties(inprops); }

    std::uint32_t Flags() const final { return matcher_.Flags(); }

private:
    // Checks current arc if available and explicit. If not available, stops. If
    // not explicit, checks next ones.
    void CheckArc() {
        for (; !matcher_.Done(); matcher_.Next()) {
            const auto &label = match_type_ == MATCH_INPUT ? matcher_.Value().ilabel : matcher_.Value().olabel;
            if (label != kNoLabel)
                return;
        }
    }

    MatcherImpl matcher_;
    MatchType match_type_; // Type of match requested.
};

} // namespace fst
